package simulator

import (
	"context"
	carpb "coolcar/proto/car_out/v1"

	"go.uber.org/zap"
)

type Controller struct {
	CarService carpb.CarServiceClient
	Subscriber Subscriber
	Logger     *zap.Logger
}

type Subscriber interface {
	Subscribe(context.Context) (chan *carpb.CarEntity, error)
}

func (c *Controller) RunSimulations(ctx context.Context) {
	res, err := c.CarService.GetCars(ctx, &carpb.GetCarsRequest{})
	if err != nil {
		c.Logger.Error("can not get cars", zap.Error(err))
		return
	}

	msgCh, err := c.Subscriber.Subscribe(ctx)
	if err != nil {
		c.Logger.Error("can not subscribe", zap.Error(err))
		return
	}

	carChans := make(map[string]chan *carpb.Car)
	for _, car := range res.Cars {
		ch := make(chan *carpb.Car)
		carChans[car.Id] = ch
		go c.SimulateCar(context.Background(), car, ch)
	}

	for carUpdate := range msgCh {
		ch := carChans[carUpdate.Id]
		if ch != nil {
			ch <- carUpdate.Car
		}
	}
}

func (c *Controller) SimulateCar(ctx context.Context, initial *carpb.CarEntity, ch chan *carpb.Car) {
	carID := initial.Id

	for update := range ch {
		if update.Status == carpb.CarStatus_UNLOCKING {
			// 开锁
			_, err := c.CarService.UpdateCar(ctx, &carpb.UpdateCarRequest{
				Id:     carID,
				Status: carpb.CarStatus_UNLOCKED,
			})
			if err != nil {
				c.Logger.Error("can not unlock car", zap.Error(err))
			}
		} else if update.Status == carpb.CarStatus_LOCKING {
			_, err := c.CarService.UpdateCar(ctx, &carpb.UpdateCarRequest{
				Id:     carID,
				Status: carpb.CarStatus_LOCKED,
			})
			if err != nil {
				c.Logger.Error("can not unlock car", zap.Error(err))
			}
		}
	}
}
